#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include<time.h>
#include<semaphore.h>

#define ARR_LENGTH 4

struct Task{
    pthread_t thread;
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    time_t arrive_time;
    clock_t arrive_clock;
    int predicted_running_time;//预测所需的运行时间
    int state;//1 is ready, 0 is running
    int* arg;
    int priority;
};

int total_time = 0;

pthread_mutex_t main_mutex;

pthread_cond_t main_cond;

int main_state = 0;

void* runner(void* arg){
    struct Task* task = (struct Task*) arg;

    pthread_mutex_lock(&(task->mutex));

    while(task->state == 1){
        pthread_cond_wait(&(task->cond),&(task->mutex));
    }
    printf("task %ld number is %d\n", pthread_self(), *(task->arg));

    main_state = 0;
    task->state = 2;

    clock_t nowclock = clock();

    total_time += nowclock-task->arrive_clock + task->predicted_running_time;

    pthread_cond_signal(&main_cond);
    pthread_mutex_unlock(&(task->mutex));
}

void FCFS(struct Task tasks[]){
    //FCFS
    for(int i = 0; i < ARR_LENGTH ; ++ i){
        pthread_mutex_lock(&(tasks[i].mutex));

        tasks[i].state = 0;
        main_state = 1;
        pthread_cond_signal(&(tasks[i].cond));

        while(main_state == 1)
            pthread_cond_wait(&(main_cond),&(tasks[i].mutex));
        
        
        pthread_mutex_unlock(&(tasks[i].mutex));
    }
}

void Priority(struct Task tasks[]){
    //Priority
    for(int i = 0; i < ARR_LENGTH ; ++ i){
        int max_pri_index = 0;
        int max_pri = 0;
        for(int j = 0; j < ARR_LENGTH  ;++ j ){
            
            if(tasks[j].state != 1 || tasks[j].arrive_time > time(NULL)){
                continue;
            }
            
            
            if(tasks[j].priority >= max_pri){
                max_pri_index = j;
                max_pri = tasks[j].priority;
            }
        }
        pthread_mutex_lock(&(tasks[max_pri_index].mutex));

        tasks[max_pri_index].state = 0;
        main_state = 1;
        printf("the priority is %d, ", max_pri);
        pthread_cond_signal(&(tasks[max_pri_index].cond));

        while(main_state == 1)
            pthread_cond_wait(&(main_cond),&(tasks[max_pri_index].mutex));
        
        
        pthread_mutex_unlock(&(tasks[max_pri_index].mutex));
    }
}

void SJF(struct Task tasks[]){
    //SJF
    for(int i = 0; i < ARR_LENGTH ; ++ i){
        int min_time_index = 0;
        int min_time = RAND_MAX;
        for(int j = 0; j < ARR_LENGTH  ;++ j ){
            
            if(tasks[j].state != 1 || tasks[j].arrive_time > time(NULL)){
                continue;
            }
            
            
            if(tasks[j].predicted_running_time <= min_time){
                min_time_index = j;
                min_time = tasks[j].priority;
            }
        }
        pthread_mutex_lock(&(tasks[min_time_index].mutex));

        tasks[min_time_index].state = 0;
        main_state = 1;
        printf("the running time is %d, ", min_time);
        pthread_cond_signal(&(tasks[min_time_index].cond));

        while(main_state == 1)
            pthread_cond_wait(&(main_cond),&(tasks[min_time_index].mutex));
        
        
        pthread_mutex_unlock(&(tasks[min_time_index].mutex));
    }
}



int main(int argc, char const *argv[]) {

    pthread_cond_init(&main_cond, NULL);
  
    int arrary[] = {0,1,2,3,4,5,6,7,8,9};

    struct Task tasks[ARR_LENGTH];

    for(int i = 0; i < ARR_LENGTH; ++ i){
        pthread_create(&(tasks[i].thread), NULL, runner, tasks + i);
        pthread_cond_init(&(tasks[i].cond), NULL);
        pthread_mutex_init(&(tasks[i].mutex), NULL);
        tasks[i].arrive_time = time(NULL);
        tasks[i].arrive_clock = clock();
        tasks[i].state = 1;//ready
        tasks[i].arg = arrary+i;
        tasks[i].priority = rand() % 25;
        tasks[i].predicted_running_time = rand() % 135;
    }

    // FCFS(tasks);
    // SJF(tasks);
    Priority(tasks);

    printf("Total time is %g, average time is %g\n", total_time*1.0/CLOCKS_PER_SEC, total_time * 1.0 / ARR_LENGTH/CLOCKS_PER_SEC);
  
    return 0;
}